/**
 * Created on [2018/12/30 下午11:57] by lidi
 * <p>
 * 项目名称: gaia-commons
 * <p>
 * 本程序版权属于福建慧政通信息科技有限公司所有。
 * <p>
 * 任何组织和个人未经福建慧政通信息科技有限公司许可与授权,不得擅自传播、复制、更改该程序的内容。
 * 本程序受版权法和国际条约的保护。如未经授权而擅自复制或传播本程序(或其中任何部分),
 * 将受到严厉的刑事及民事制裁，并将在法律许可的范围内受到最大可能的起诉!
 * <p>
 * ©2018 福建慧政通信息科技有限公司
 */

package com.lry.util.captcha2;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import javax.imageio.ImageIO;

/**
 * 实现了图像验证码的基本操作。
 *
 * @author 李迪
 */
public abstract class BaseImageCaptcha implements ImageCaptcha {

    /**
     * 验证码图像默认格式。
     */
    public static final String DEFAULT_IMG_FORMAT = "png";

    /**
     * 验证码图像宽度（px）。
     */
    protected int width = 120;

    /**
     * 验证码图像高度（px）。
     */
    protected int height = 36;

    /**
     * 验证码字体。
     */
    protected Font font = new Font("Arial", Font.BOLD, 32);

    /**
     * 验证码干扰元素个数
     */
    protected int interfereCount = 26;

    /**
     * 默认构造函数。
     */
    public BaseImageCaptcha() {
    }

    /**
     * 通过图像验证码的宽度、高度构造图像验证码对象。
     *
     * @param width  图像验证码的宽度（px）
     * @param height 图像验证码的宽度（px）
     */
    public BaseImageCaptcha(int width, int height) {
        this.width = width;
        this.height = height;
    }

    /**
     * 通过图像验证码的宽度、高度、字体构造图像验证码对象。
     *
     * @param width  图像验证码的宽度（px）
     * @param height 图像验证码的宽度（px）
     * @param font   图像验证码所用的字体。
     */
    public BaseImageCaptcha(int width, int height, Font font) {
        this.width = width;
        this.height = height;

        if (font != null) {
            this.font = font;
        }
    }

    /**
     * 生成验证码图像。
     *
     * @param code 验证码内容。
     * @return 验证码图像。
     */
    @Override
    public BufferedImage generate(String code) {
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        final ThreadLocalRandom random = ThreadLocalRandom.current();
        Graphics2D g2d = bufferedImage.createGraphics();
        // 填充背景色
        g2d.setColor(Color.white);
        g2d.fillRect(0, 0, width, height);

        // 绘制干扰线
        drawInterfere(g2d);

        g2d.setFont(font);
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        final FontMetrics metrics = g2d.getFontMetrics();
        int minY = metrics.getAscent() - metrics.getLeading() - metrics.getDescent();
        int len = code.length();
        int charWidth = width / len;
        for (int i = 0; i < len; i++) {
            // 产生随机的颜色值，让输出的每个字符的颜色值都将不同。
            g2d.setColor(randomColor(random));
            g2d.drawString(String.valueOf(code.charAt(i)), i * charWidth, random.nextInt(minY, height));
        }

        // 关闭资源
        if (g2d != null) {
            g2d.dispose();
        }

        return bufferedImage;
    }

    /**
     * 获取验证码图像所用的字体。
     *
     * @return 验证码图像所用的字体。
     */
    @Override
    public Font getFont() {
        return font;
    }

    /**
     * 获取验证码图像宽度。
     *
     * @return 验证码图像宽度（px）。
     */
    @Override
    public int getWidth() {
        return width;
    }

    /**
     * 获取验证码图像高度。
     *
     * @return 验证码图像高度（px）。
     */
    @Override
    public int getHeight() {
        return height;
    }

    /**
     * 将图像验证码输出到指定的输出流。
     *
     * @param code         验证码内容。
     * @param imgFormat    图像格式。
     * @param outputStream 验证码输出流。
     * @throws IOException IO异常。
     */
    @Override
    public void write(String code, String imgFormat, OutputStream outputStream) throws IOException {
        BufferedImage bufferedImage = generate(code);
        if (bufferedImage != null) {
            ImageIO.write(bufferedImage, imgFormat, outputStream);
        }
    }

    /**
     * 将验证码输出到指定的输出流。
     *
     * @param code         验证码内容。
     * @param outputStream 验证码输出流。
     * @throws IOException IO异常。
     */
    @Override
    public void write(String code, OutputStream outputStream) throws IOException {
        write(code, DEFAULT_IMG_FORMAT, outputStream);
    }

    /**
     * 绘制干扰图形。
     *
     * @param g2d 图形上下文。
     */
    protected abstract void drawInterfere(final Graphics2D g2d);

    /**
     * 通过随机对象产生随机颜色。
     *
     * @param random 随机对象。
     * @return 通过随机对象产生的随机颜色。
     */
    protected Color randomColor(final Random random) {
        Random tmpRandom = random;
        if (tmpRandom == null) {
            tmpRandom = ThreadLocalRandom.current();
        }

        return new Color(tmpRandom.nextInt(255), tmpRandom.nextInt(255), tmpRandom.nextInt(255));
    }
}
